home *** CD-ROM | disk | FTP | other *** search
/ Info-Mac 4 / Info_Mac IV CD-ROM (Pacific HiTech Inc.)(August 1994).iso / Development / Source / DBL Pascal Library / TransactionMgr / Transactions
Text File  |  1993-04-14  |  10KB  |  292 lines

  1. unit Transactions;
  2.  
  3. {The Transactions unit provides a framework for combining groups of operations on related}
  4. {data into a single transaction. The transaction may be committed, cancelled, undone and}
  5. {redone using a standard interface to a user-supplied routine that implements the semantics}
  6. {of these transaction operations in terms of the user data and operations.}
  7. {}
  8. {This unit does not impose a particular semantics on transactions — semantics are the domain}
  9. {of a user-supplied action routine. This framework can support both "update-on-commit" and}
  10. {"retract-on-cancel" semantics. Update-on-commit semantics implies that any data created}
  11. {during a transaction is not committed to the “live” database until the transaction is committed.}
  12. {Retract-on-cancel means that the “live” database is modified during the transaction, and}
  13. {restored to its pre-transaction state if the transaction is cancelled. You may also implement}
  14. {a mixed strategy. For example, you could use "retract-on-cancel" for added and changed}
  15. {records, but "update-on-commit" for deletions.}
  16.  
  17. interface
  18.  
  19.     type
  20.         TransactionHandle = Handle;
  21.         TransactionOp = (    {}
  22.             transactionOpCommit,        {}
  23.             transactionOpCancel,        {}
  24.             transactionOpUndo,            {}
  25.             transactionOpRedo,            {}
  26.             transactionOpDispose        {}
  27.             );
  28.  
  29. {NewTransaction creates a new transaction record. The record is valid until DisposeTransaction.}
  30. {The transaction record may be reused for multiple transactions; it is initialized by each call to}
  31. {BeginTransaction.}
  32.     function NewTransaction: TransactionHandle;
  33.  
  34. {BeginTransaction initializes the given transaction record for a new transaction, and records}
  35. {the user’s actionProc which will be called to process entries on behalf of EndTransaction,}
  36. {UndoTransaction, and RedoTransaction. The user’s actionProc is declared as follows:}
  37. {    procedure ActionProc (userData, userAction: Longint; operation: TransactionOp; cancelled, undone: Boolean);    }
  38.     procedure BeginTransaction (transaction: TransactionHandle;
  39.                                     actionProc: ProcPtr);
  40.  
  41. {RecordTransactionStep remembers one step of the current transaction. A step is represented}
  42. {by a datum and an action. Any memory needed to store information about the step must be}
  43. {allocated by the caller, and disposed by the user action proc when called with the dispose op.}
  44.     function RecordTransactionStep (transaction: TransactionHandle;
  45.                                     transData, actionCode: univ Longint): OSErr;
  46.  
  47. {TransactionExists searches the transaction record and returns true if a matched transaction is}
  48. {found. Match criteria is established by a user supplied function. The match function must not}
  49. {have side effects on the transaction, userData or userAction. The search begins with the most}
  50. {recent transaction step if fromEnd is true.}
  51.     function TransactionExists (transaction: TransactionHandle;
  52.                                     fromEnd: Boolean;
  53.                                     function MatchFunc (userData, userAction: Longint): Boolean): Boolean;
  54.  
  55. {EndTransaction marks the end of all steps which comprise the current transaction. The}
  56. {transaction may be committed or not — the action proc is called with the appropriate op code.}
  57. {The action proc is called on step data in the same order as recorded for a commit, and in the}
  58. {reverse order for a cancel. A cancelled transaction becomes non-undoable, even if it was}
  59. {specified as undoable.}
  60.     procedure EndTransaction (transaction: TransactionHandle;
  61.                                     commit: Boolean);
  62.  
  63. {TransactionCancelled returns true if the transaction was cancelled (not committed).}
  64.     function TransactionCancelled (transaction: TransactionHandle): Boolean;
  65.  
  66. {SetTransactionUndoable specifies whether a transaction is undoable. Newly-created}
  67. {transactions are not undoable. The state may be changed at any time.}
  68.     procedure SetTransactionUndoable (transaction: TransactionHandle;
  69.                                     undoable: Boolean);
  70.  
  71. {TransactionUndoable returns true if the transaction is undoable (or redoable).}
  72. {The result is the same even if the transaction has been undone.}
  73.     function TransactionUndoable (transaction: TransactionHandle): Boolean;
  74.  
  75. {If the transaction is undoable, and has not previously been undone, UndoTransaction calls the}
  76. {action proc using the undo op code with the step information in forward order.}
  77.     procedure UndoTransaction (transaction: TransactionHandle);
  78.  
  79. {TransactionUndone returns True if the transaction has been undone.}
  80.     function TransactionUndone (transaction: TransactionHandle): Boolean;
  81.  
  82. {If the transaction is undoable, and has been undone, RedoTransaction calls the}
  83. {action proc using the redo op code with the step information in reverse order.}
  84.     procedure RedoTransaction (transaction: TransactionHandle);
  85.  
  86. {DisposeTransaction calls the action proc using the dispose op code with the step information}
  87. {in forward order, then disposes of the transaction record and sets it to nil.}
  88.     procedure DisposeTransaction (var transaction: TransactionHandle);
  89.  
  90. implementation
  91.  
  92.     type
  93.         TransactionEntry = record
  94.                 userData: Longint;
  95.                 userAction: Longint;
  96.             end;
  97.         TransactionRecHeader = record
  98.                 userProc: ProcPtr;
  99.                 count: Integer;
  100.                 canUndo, undone, cancelled: Boolean;
  101.             end;
  102.         TransactionRec = record
  103.                 header: TransactionRecHeader;
  104.                 entries: array[1..1] of TransactionEntry;
  105.             end;
  106.         TransactionRecPtr = ^TransactionRec;
  107.         TransactionRecHdl = ^TransactionRecPtr;
  108.  
  109.     function NewTransaction: TransactionHandle;
  110.     begin
  111.         NewTransaction := NewHandleClear(SIZEOF(TransactionRecHeader));
  112.     end;
  113.  
  114.     procedure BeginTransaction (transaction: TransactionHandle;
  115.                                     actionProc: ProcPtr);
  116.     begin
  117.         with TransactionRecHdl(transaction)^^.header do
  118.             begin
  119.                 userProc := actionProc;
  120.                 count := 0;
  121.                 canUndo := False;
  122.                 undone := False;
  123.                 cancelled := False;
  124.             end;
  125.         SetHandleSize(Handle(transaction), SIZEOF(TransactionRecHeader));
  126.     end;
  127.  
  128.     procedure CallActionProc (userData, userAction: Longint;
  129.                                     operation: TransactionOp;
  130.                                     cancelled, undone: Boolean;
  131.                                     actionProc: ProcPtr);
  132.     inline
  133.         $205F,    {    move.l (a7)+,a0}
  134.         $4E90;    {    jsr (a0)}
  135.  
  136.     type
  137.         OpDirection = (firstToLast, lastToFirst);
  138.  
  139.     procedure ProcessTransactionOp (transaction: TransactionHandle;
  140.                                     operation: TransactionOp;
  141.                                     direction: OpDirection);
  142.         var
  143.             i: Integer;
  144.     begin
  145.         HLock(transaction);
  146.         with TransactionRecHdl(transaction)^^, header do
  147.             if direction = lastToFirst then
  148.                 for i := count downto 1 do
  149. {$PUSH}
  150. {$R-}
  151.                     with entries[i] do
  152. {$POP}
  153.                         CallActionProc(userData, userAction, operation, cancelled, undone, userProc)
  154.             else
  155.                 for i := 1 to count do
  156. {$PUSH}
  157. {$R-}
  158.                     with entries[i] do
  159. {$POP}
  160.                         CallActionProc(userData, userAction, operation, cancelled, undone, userProc);
  161.         HUnlock(transaction);
  162.     end;
  163.  
  164.     function TransactionExists (transaction: TransactionHandle;
  165.                                     fromEnd: Boolean;
  166.                                     function MatchFunc (userData, userAction: Longint): Boolean): Boolean;
  167.         var
  168.             i: Integer;
  169.             result: Boolean;
  170.     begin
  171.         result := False;
  172.         HLock(Handle(transaction));
  173.         with TransactionRecHdl(transaction)^^, header do
  174.             if fromEnd then
  175.                 begin
  176.                     for i := count downto 1 do
  177. {$PUSH}
  178. {$R-}
  179.                         with entries[i] do
  180. {$POP}
  181.                             if MatchFunc(userData, userAction) then
  182.                                 begin
  183.                                     result := True;
  184.                                     Leave;
  185.                                 end;
  186.                 end
  187.             else
  188.                 begin
  189.                     for i := 1 to count do
  190. {$PUSH}
  191. {$R-}
  192.                         with entries[i] do
  193. {$POP}
  194.                             if MatchFunc(userData, userAction) then
  195.                                 begin
  196.                                     result := True;
  197.                                     Leave;
  198.                                 end;
  199.                 end;
  200.         HUnlock(Handle(transaction));
  201.         TransactionExists := result;
  202.     end;
  203.  
  204.     procedure EndTransaction (transaction: TransactionHandle;
  205.                                     commit: Boolean);
  206.     begin
  207.         if commit then
  208.             ProcessTransactionOp(transaction, transactionOpCommit, firstToLast)
  209.         else
  210.             begin
  211.                 ProcessTransactionOp(transaction, transactionOpCancel, lastToFirst);
  212.                 SetTransactionUndoable(transaction, False);
  213.                 TransactionRecHdl(transaction)^^.header.cancelled := True;
  214.             end;
  215.     end;
  216.  
  217.     function TransactionCancelled (transaction: TransactionHandle): Boolean;
  218.     begin
  219.         TransactionCancelled := TransactionRecHdl(transaction)^^.header.cancelled;
  220.     end;
  221.  
  222.     procedure SetTransactionUndoable (transaction: TransactionHandle;
  223.                                     undoable: Boolean);
  224.     begin
  225.         with TransactionRecHdl(transaction)^^.header do
  226.             if not cancelled then
  227.                 canUndo := undoable;
  228.     end;
  229.  
  230.     function TransactionUndoable (transaction: TransactionHandle): Boolean;
  231.     begin
  232.         TransactionUndoable := TransactionRecHdl(transaction)^^.header.canUndo;
  233.     end;
  234.  
  235.     procedure UndoTransaction (transaction: TransactionHandle);
  236.     begin
  237.         with TransactionRecHdl(transaction)^^.header do
  238.             if canUndo and not undone then
  239.                 begin
  240.                     ProcessTransactionOp(transaction, transactionOpUndo, lastToFirst);
  241.                     TransactionRecHdl(transaction)^^.header.undone := True;
  242.                 end;
  243.     end;
  244.  
  245.     function TransactionUndone (transaction: TransactionHandle): Boolean;
  246.     begin
  247.         TransactionUndone := TransactionRecHdl(transaction)^^.header.undone;
  248.     end;
  249.  
  250.     procedure RedoTransaction (transaction: TransactionHandle);
  251.     begin
  252.         with TransactionRecHdl(transaction)^^.header do
  253.             if canUndo and undone then
  254.                 begin
  255.                     ProcessTransactionOp(transaction, transactionOpRedo, firstToLast);
  256.                     TransactionRecHdl(transaction)^^.header.undone := False;
  257.                 end;
  258.     end;
  259.  
  260.     procedure DisposeTransaction (var transaction: TransactionHandle);
  261.     begin
  262.         ProcessTransactionOp(transaction, transactionOpDispose, lastToFirst);
  263.         DisposeHandle(Handle(transaction));
  264.         transaction := nil;
  265.     end;
  266.  
  267.     function RecordTransactionStep (transaction: TransactionHandle;
  268.                                     transData, actionCode: univ Longint): OSErr;
  269.     begin
  270.         SetHandleSize(Handle(transaction), GetHandleSize(Handle(transaction)) + SIZEOF(TransactionEntry));
  271.         if MemError <> noErr then
  272.             begin
  273.                 RecordTransactionStep := MemError;
  274.                 Exit(RecordTransactionStep);
  275.             end
  276.         else
  277.             with TransactionRecHdl(transaction)^^, header do
  278.                 if userProc <> nil then
  279.                     begin
  280.                         count := count + 1;
  281. {$PUSH}
  282. {$R-}
  283.                         with entries[count] do
  284. {$POP}
  285.                             begin
  286.                                 userData := transData;
  287.                                 userAction := actionCode;
  288.                             end;
  289.                     end;
  290.     end;
  291.  
  292. end.